using System;
using System.Collections;
using NaughtyAttributes;
using Unity.Entities;
using UnityEngine;

namespace Gj
{
    public class AnimationHelper : MonoBehaviour
    {
        public interface Notice
        {
            void OnStart(string key);
            void OnEnd(string key);
        }
        public string key;

        public GameObject target;
        public bool self;
        protected GameObject GameObject
        {
            get
            {
                if (target != null) return target;
                return gameObject;
            }
        }

        protected Transform Transform
        {
            get
            {
                if (target != null) return target.transform;
                return transform;
            }
        }

        public RectTransform rect;
        private RectTransform _rect;
        protected RectTransform Rect 
        {
            get 
            {
                if (rect != null) return rect;
                if (_rect == null) _rect = GameObject.GetComponent<RectTransform>();
                return _rect;
            }
        }

        public bool debug;
        public float valueFrom;
        public float valueTo = 1.0f;

        public float time;
        public float delay;

        public iTween.LoopType loopType = iTween.LoopType.none;
        public iTween.EaseType easeType = iTween.EaseType.linear;

        public bool ignoreTimescale;

        public bool autoPlay = true;

        public bool forward;
        public bool fastForward;
        public bool fastBackward;
        public bool destroy;

        public bool system = true;
        public Notice notice;
        public AudioClip clip;
        public SoundType soundType;
        private Entity entity;

        private bool play;

        private void Awake()
        {
            Init();
            if (autoPlay)
                Forward();
        }

        private void Update()
        {
            if (!system) return;
            if (!play) return;
            if (entity == Entity.Null) return;
            var d = ObjectService.EntityManager.GetComponentData<AnimationData>(entity);
            if (debug) Debug.LogError(d.value);
            if (d.end)
            {
                Destroy();
                OnEnd();
            }
            else
            {
                OnUpdate(d.value);
            }
        }

        private void OnEnable()
        {
            if (!system) return;
            if (!play) return;
            if (entity == Entity.Null) return;
            var d = ObjectService.EntityManager.GetComponentData<AnimationData>(entity);
            d.stop = false;
            ObjectService.EntityManager.AddComponentData(entity, d);
        }

        private void OnDisable()
        {
            if (!system) return;
            if (!play) return;
            if (entity == Entity.Null) return;
            var d = ObjectService.EntityManager.GetComponentData<AnimationData>(entity);
            d.stop = true;
            ObjectService.EntityManager.AddComponentData(entity, d);
        }

        private void OnDestroy()
        {
        }

        protected virtual void Init()
        {
        }

        public void Restore()
        {
            Stop();
            OnUpdate(valueFrom);
        }

        public void SetNotice(Notice n)
        {
            notice = n;
        }

        public void SetTarget(GameObject t)
        {
            if (self) return;
            target = t;
        }

        [Button]
        public void Forward()
        {
            if (debug) Debug.LogError("Forward");
            forward = true;
            if (fastForward)
            {
                if (play) Stop();
                OnUpdate(valueTo);
            }
            else
            {
                Play(valueFrom, valueTo);
            }
        }

        [Button]
        public void Backward()
        {
            if (debug) Debug.LogError("Backward");
            forward = false;
            if (fastBackward)
            {
                if (play) Stop();
                OnUpdate(valueFrom);
            }
            else
            {
                Play(valueTo, valueFrom);
            }
        }

        public void Play()
        {
            Forward();
        }

        public void ResumeSystem()
        {
            if (entity == Entity.Null) entity = ObjectService.EntityManager.CreateEntity();
            AnimationData data = ObjectService.EntityManager.GetComponentData<AnimationData>(entity);
            data.stop = false;
            ObjectService.EntityManager.SetComponentData(entity, data);
        }

        public void PauseSystem()
        {
            if (entity == Entity.Null) entity = ObjectService.EntityManager.CreateEntity();
            AnimationData data = ObjectService.EntityManager.GetComponentData<AnimationData>(entity);
            data.stop = true;
            ObjectService.EntityManager.SetComponentData(entity, data);
        }

        public void StopSystem()
        {
            if (entity == Entity.Null) entity = ObjectService.EntityManager.CreateEntity();
            ObjectService.EntityManager.RemoveComponent<AnimationData>(entity);
            play = false;
        }

        public void ResumeScript()
        {
            iTween.Resume(GameObject);
        }

        public void PauseScript()
        {
            iTween.Pause(GameObject);
        }

        public void StopScript()
        {
            iTween.Stop(GameObject);
            play = false;
        }

        public void Pause()
        {
            if (system)
                PauseSystem();
            else
                PauseScript();
        }

        public void Resume()
        {
            if (system)
                ResumeSystem();
            else
                ResumeScript();
        }

        public void Stop()
        {
            if (system)
                StopSystem();
            else
                StopScript();
        }

        public void End()
        {
            Stop();
            if (forward)
                OnUpdate(valueTo);
            else
                OnUpdate(valueFrom);
        }

        public void Play(float from, float to)
        {
            play = true;
            
            if (soundType != SoundType.None)
                Game.single.PlayEFF(soundType);
            else if (clip != null) Game.single.PlayEFF(clip);
            
            if (system)
                PlayBySystem(from, to);
            else
                PlayByScript(from, to);
        }

        private void PlayByScript(float from, float to)
        {
            var ht = new Hashtable();

            ht.Add("from", from);
            ht.Add("to", to);
            ht.Add("time", time);
            ht.Add("delay", delay);

            ht.Add("looptype", loopType);
            ht.Add("easetype", easeType);

            ht.Add("onstart", (Action<object>)(newVal => { OnStart(); }));
            ht.Add("onupdate", (Action<object>)(newVal => { OnUpdate((float)newVal); }));
            ht.Add("oncomplete", (Action<object>)(newVal => { OnEnd(); }));

            ht.Add("ignoretimescale", ignoreTimescale);

            iTween.ValueTo(GameObject, ht);
        }

        private void PlayBySystem(float from, float to)
        {
            if (entity == Entity.Null) entity = ObjectService.EntityManager.CreateEntity();
            ObjectService.EntityManager.AddComponentData(entity,
                new AnimationData
                {
                    from = from, to = to, value = from, delay = delay, time = time,
                    loopType = MathTools.ConvertLoopType(loopType), easeType = MathTools.ConvertEaseType(easeType)
                });
            OnStart();
        }

        protected virtual void OnStart()
        {
            if (debug) Debug.LogError("OnStart");
            if (notice != null) notice.OnStart(key);
        }

        protected virtual void OnEnd()
        {
            if (debug) Debug.LogError("OnEnd");
            if (loopType == iTween.LoopType.none) play = false;
            if (destroy) Destroy(GameObject);
            if (notice != null) notice.OnEnd(key);
        }

        protected virtual void OnUpdate(float value)
        {
        }

        private void Destroy()
        {
            ObjectService.EntityManager.DestroyEntity(entity);
            entity = Entity.Null;
        }
    }
}